/*
 * Copyright 2007 Agile Plugins Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.agileplugins.monkey;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataConstants;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;

public class HippieCompletionAction extends AnAction {
    private String searchTerm;
    private String lastMatch;
    private int lastOffset = -1;
    private int matchOffset = -1;

    public void actionPerformed(AnActionEvent anactionevent) {
        Project project = (Project) anactionevent.getDataContext().getData(DataConstants.PROJECT);
        Editor editor = (Editor) anactionevent.getDataContext().getData(DataConstants.EDITOR);
        boolean searchUp = anactionevent.getInputEvent().paramString().indexOf("keyText=K") != -1;
        if (editor == null) {
            return;
        }

        int offset = editor.getCaretModel().getOffset();
        String text = editor.getDocument().getText();
        int startOffset = findStartOfWord(text, offset);

        if (offset != lastOffset) {
            searchTerm = text.substring(startOffset, offset);
            matchOffset = startOffset;
            lastMatch = searchTerm;
        }

        while (matchOffset >= 0) {
            int newMatch = searchForWord(text, searchUp);
            if (newMatch == -1) {
                return;
            }
            matchOffset = newMatch;

            if (matchOffset == 0 || !isWordChar(text.charAt(matchOffset - 1))) {
                int endOffset = findEndOfWord(text, matchOffset);
                if (endOffset > matchOffset) {
                    String replaceString = text.substring(matchOffset, endOffset);
                    if (!replaceString.equals(lastMatch)) {
                        lastMatch = replaceString;
                        substitute(editor, startOffset, offset, replaceString, project, searchUp);
                        return;
                    }
                }
            }
        }

    }

    private int searchForWord(String text, boolean searchUp) {
        int pos = searchUp ? matchOffset - 1 : matchOffset + lastMatch.length();
        if (searchTerm.length() == 0) {
            int inc = searchUp ? -1 : 1;
            while (pos >= 0 && pos < text.length() && !isWordChar(text.charAt(pos))) {
                pos += inc;
            }
            if (pos >= text.length()) {
                return -1;
            }
            if (searchUp) {
                pos = findStartOfWord(text, pos);
            }
            return pos;
        } else if (searchUp) {
            return text.lastIndexOf(searchTerm, pos);
        } else {
            return text.indexOf(searchTerm, pos);
        }
    }

    private void substitute(final Editor editor, final int startOffset, final int offset, final String replaceString, Project project, boolean searchUp) {
        final Runnable runnable = new Runnable() {
            public void run() {
                Document document = editor.getDocument();
                document.replaceString(startOffset, offset, replaceString);
                int moveOffset = replaceString.length() - offset + startOffset;
                lastOffset = offset + moveOffset;
                editor.getCaretModel().moveToOffset(lastOffset);
                if (matchOffset > startOffset) {
                    matchOffset += moveOffset;
                }
            }

        };

        CommandProcessor.getInstance().executeCommand(project,
                new Runnable() {
                    public void run() {
                        ApplicationManager.getApplication().runWriteAction(runnable);
                    }
                }, "HippieCompletion", null);
    }

    private int findEndOfWord(String text, int offset) {
        while (offset < text.length() && isWordChar(text.charAt(offset))) {
            offset++;
        }
        return offset;
    }

    private int findStartOfWord(String text, int offset) {
        while (offset > 0 && isWordChar(text.charAt(offset - 1))) {
            offset--;
        }
        return offset;
    }

    private boolean isWordChar(char ch) {
        return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_';
    }
}
